import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PlayerStatisticsMain {
  private final static boolean DEBUG = false;

  private final static String URL_TO_ANALYZE_KM =
      "http://vereine.fussballoesterreich.at/ooe/SportunionWindhaagBeiPerg/835826322304097996_835826325793759065~835826325793759083~951655964933355675~915354195985455850~915354196522326772_915354196522326772,de.html";

  private final static String URL_TO_ANALYZE_RES =
      "http://vereine.fussballoesterreich.at/ooe/SportunionWindhaagBeiPerg/835826322304097996_835826325793759065~835826325793759083~951655964933355675~915354196522326774~915354196522326784_915354196522326784,de.html";

  private final static String PLAYER_DETAIL_PATTERN =
      "\"spielerDetailsUrl\":\"([^\"]+)\",.*?,\"spielerName\":\"([^\"]+)\"";

  private final static String PLAYER_APPEARANCES_PATTERN =
      "<tr class=\"a\">.*?Eins&auml;tze.*?&nbsp;.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?&nbsp;.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?</tr>";

  private final static String PLAYER_GOALS_PATTERN =
      "<tr class=\"a\">.*?Tore.*?&nbsp;.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?&nbsp;.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?</tr>";

  private final static String PLAYER_MINUTES_PATTERN =
      "<tr class=\"a\">.*?Einsatzminuten.*?&nbsp;.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?&nbsp;.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?</tr>";

  private final static String PLAYER_MINUTES_MEAN_PATTERN =
      "<tr class=\"a\">.*?Einsatzminuten je Spiel.*?&nbsp;.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?&nbsp;.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?<td>([^<]+)</td>.*?</tr>";

  // <tr class="b"> <td>Ein- / Auswechslungen</td> <td class="space">&nbsp;</td> <td> 2 / - </td>
  // <td> 5 / 5 </td> <td> - / - </td> <td class="space">&nbsp;</td> <td> 2 / - </td> <td> 14 / 5
  // </td> <td> 5 / 1 </td> </tr>

  private static long startTime = System.nanoTime();


  public static void main(final String[] args) throws Exception {
    PlayerStatisticsMain main = new PlayerStatisticsMain();

    Collection<PlayerDetails> players = new HashSet<PlayerDetails>();

    players.addAll(main.analyseTeamPage(URL_TO_ANALYZE_KM));
    players.addAll(main.analyseTeamPage(URL_TO_ANALYZE_RES));

    players = main.sort(players);

    main.output(players);
  }

  private Collection<PlayerDetails> sort(Collection<PlayerDetails> players) {
    List<PlayerDetails> playersList = new LinkedList<PlayerDetails>(players);
    Collections.sort(playersList, PlayerDetails_Comparator);
    return playersList;
  }

  private void output(Collection<PlayerDetails> players) {
    System.out.println("\n\n\n");
    System.out.println("\t\tKM\t\t\tRes\t\t\tGesamt\t");
    System.out
        .println("\tName\tSpiele\tMinuten\tTore\tSpiele\tMinuten\tTore\tSpiele\tMinuten\tTore\t");
    for (PlayerDetails player : players) {
      System.out.print("\t");
      System.out.print(player.name + "\t");
      System.out.print(player.statsKM.appearances + "\t");
      System.out.print(player.statsKM.minutes + "\t");
      System.out.print(player.statsKM.goals + "\t");
      System.out.print(player.statsRES.appearances + "\t");
      System.out.print(player.statsRES.minutes + "\t");
      System.out.print(player.statsRES.goals + "\t");
      System.out.print((player.statsKM.appearances + player.statsRES.appearances) + "\t");
      System.out.print((player.statsKM.minutes + player.statsRES.minutes) + "\t");
      System.out.print((player.statsKM.goals + player.statsRES.goals) + "\t");
      System.out.println("");
    }
  }


  private Set<PlayerDetails> analyseTeamPage(final String url) {
    debug(" * " + url);

    Set<PlayerDetails> playerSet = new HashSet<PlayerDetails>();

    String teamPageContent = readContent(url);

    final Pattern playerDetailPattern = Pattern.compile(PLAYER_DETAIL_PATTERN);
    final Matcher playerDetailMatcher = playerDetailPattern.matcher(teamPageContent);

    int i = 1;

    while (playerDetailMatcher.find()) {
      final String playerDetailUrl =
          "http://vereine.fussballoesterreich.at"
              + playerDetailMatcher.group(1).replace("0.htm", "2.htm");
      final String playerName = playerDetailMatcher.group(2);

      System.out.println("(" + (i++) + ") " + playerName);

      PlayerDetails player = analysePlayerDetails(playerDetailUrl, playerName);
      playerSet.add(player);
    }

    return playerSet;
  }

  private PlayerDetails analysePlayerDetails(String url, String playerName) {
    debug(" * " + url);

    PlayerDetails player = new PlayerDetails(playerName, url);

    String playerDetailContent = readContent(url);

    final Pattern playerAppearancesPattern = Pattern.compile(PLAYER_APPEARANCES_PATTERN);
    final Matcher playerAppearancesMatcher = playerAppearancesPattern.matcher(playerDetailContent);
    final Pattern playerGoalsPattern = Pattern.compile(PLAYER_GOALS_PATTERN);
    final Matcher playerGoalsMatcher = playerGoalsPattern.matcher(playerDetailContent);
    final Pattern playerMinutesPattern = Pattern.compile(PLAYER_MINUTES_PATTERN);
    final Matcher playerMinutesMatcher = playerMinutesPattern.matcher(playerDetailContent);
    final Pattern playerMinutesMeanPattern = Pattern.compile(PLAYER_MINUTES_MEAN_PATTERN);
    final Matcher playerMinutesMeanMatcher = playerMinutesMeanPattern.matcher(playerDetailContent);

    if (playerAppearancesMatcher.find() && playerGoalsMatcher.find() && playerMinutesMatcher.find()
        && playerMinutesMeanMatcher.find()) {
      player.statsKM =
          new PlayerStatistics(playerAppearancesMatcher.group(4), playerGoalsMatcher.group(4),
              playerMinutesMatcher.group(4), playerMinutesMeanMatcher.group(4));
      player.statsRES =
          new PlayerStatistics(playerAppearancesMatcher.group(5), playerGoalsMatcher.group(5),
              playerMinutesMatcher.group(5), playerMinutesMeanMatcher.group(5));
      player.statsNW =
          new PlayerStatistics(playerAppearancesMatcher.group(6), playerGoalsMatcher.group(6),
              playerMinutesMatcher.group(6), playerMinutesMeanMatcher.group(6));
    }

    return player;
  }

  private static String readContent(final String urlName) {
    return readContent(urlName, null);
  }


  /**
   * Reads the content of URL.
   * 
   * @param urlName the name of the URL to read
   * @return the content as a string
   */
  private static String readContent(final String urlName, final String exitCriterion) {
    final long startTimeRead = elapsedTimeMs();
    debug(String.format(">> Start reading of %s \n", urlName));
    HttpURLConnection con = null;
    URL url = null;
    try {
      url = new URL(urlName);

      final long tmpTimeRead1 = elapsedTimeMs();
      con = (HttpURLConnection) url.openConnection();
      final long tmpTimeRead2 = elapsedTimeMs();
      debug("openConnection: " + (tmpTimeRead2 - tmpTimeRead1));

      final BufferedReader reader =
          new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
      final StringBuilder sb = new StringBuilder();
      String line = reader.readLine();
      while (line != null) {
        sb.append(line);
        line = reader.readLine();
        if (exitCriterion != null && line != null && line.contains(exitCriterion)) {
          sb.append(line);
          break;
        }
      }

      return sb.toString();
    } catch (final Exception ex) {
      debug(String.format(
          "+++++++++ time: %8d - exception in reading: %s (exception message: %s) ++++++++++ \n",
          elapsedTimeMs(), url.toString(), ex.getMessage()));
      return null;
    } finally {
      try {
        con.disconnect();
      } catch (final Exception e) {
      }
      final long tmpTimeRead = elapsedTimeMs() - startTimeRead;
      debug(String.format(">> End reading of %s (duration = %d ms) \n", urlName, tmpTimeRead));
    }
  }


  private static void debug(final String msg) {
    if (PlayerStatisticsMain.DEBUG) {
      System.out.println(msg);
    }
  }


  /**
   * Returns the elapsed time since the start of the analysis in ms.
   * 
   * @return time since start of analysis in ms
   */
  private static long elapsedTimeMs() {
    // return System.currentTimeMillis() - startTime;
    return (System.nanoTime() - startTime) / 1000 / 1000;
  }

  private static Comparator<PlayerDetails> PlayerDetails_Comparator =
      new Comparator<PlayerDetails>() {

        @Override
        public int compare(PlayerDetails details1, PlayerDetails details2) {
          // sort by KM apps, Res apps, name
          int KMAppDiff = details2.statsKM.appearances - details1.statsKM.appearances;
          if (KMAppDiff != 0) return KMAppDiff;
          int RESAppDiff = details2.statsRES.appearances - details1.statsRES.appearances;
          if (RESAppDiff != 0) return RESAppDiff;
          return details1.name.compareTo(details2.name);
        }
      };


  private class PlayerDetails {
    String name;
    String url;
    PlayerStatistics statsKM;
    PlayerStatistics statsRES;
    PlayerStatistics statsNW;

    public PlayerDetails(String name, String url) {
      this.name = name;
      this.url = url;
    }

    private PlayerStatisticsMain getOuterType() {
      return PlayerStatisticsMain.this;
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + getOuterType().hashCode();
      result = prime * result + ((name == null) ? 0 : name.hashCode());
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj) return true;
      if (obj == null) return false;
      if (getClass() != obj.getClass()) return false;
      PlayerDetails other = (PlayerDetails) obj;
      if (!getOuterType().equals(other.getOuterType())) return false;
      if (name == null) {
        if (other.name != null) return false;
      } else if (!name.equals(other.name)) return false;
      return true;
    }
  }

  private class PlayerStatistics {
    int appearances;
    int goals;
    int minutes;
    int minutesMean;

    PlayerStatistics(String appearances, String goals, String minutes, String minutesMean) {
      this.appearances = convert(appearances);
      this.goals = convert(goals);
      this.minutes = convert(minutes);
      this.minutesMean = convert(minutesMean);
    }

    private int convert(String str) {
      try {
        return Integer.parseInt(str.replace(".", ""));
      } catch (NumberFormatException nfe) {
      }
      return 0;
    }
  }

}
